ποΈGitΠ―ΡΠ°ποΈ
docs/companion_protocol.md a7426de0bf938c089531e5e2b2177d989fbc07e7 (a7426de0) Text, 34.16 KB
Companion Protocol
β’ Last Updated: 2026-03-08
β’ Protocol Version: Companion Firmware v1.12.0+
β NOTE: This document is still in development. Some information may be inaccurate.
This document provides a comprehensive guide for communicating with MeshCore devices over Bluetooth Low Energy (BLE).
It is platform-agnostic and can be used for Android, iOS, Python, JavaScript, or any other platform that supports BLE.
Official Libraries
Please see the following repos for existing MeshCore Companion Protocol libraries.
β’ JavaScript: https://github.com/meshcore-dev/meshcore.js
β’ Python: https://github.com/meshcore-dev/meshcore_py
Important Security Note
All secrets, hashes, and cryptographic values shown in this guide are example values only.
β’ All hex values, public keys and hashes are for demonstration purposes only
β’ Never use example secrets in production
β’ Always generate new cryptographically secure random secrets
β’ Please implement proper security practices in your implementation
β’ This guide is for protocol documentation only
Table of Contents
BLE Connection
Service and Characteristics
MeshCore Companion devices expose a BLE service with the following UUIDs:
β’ Service UUID: T3838386E400001-B5A3-F393-E0A9-E50E24DCCA9E
β’ RX Characteristic (App β Firmware): T3838386E400002-B5A3-F393-E0A9-E50E24DCCA9E
β’ TX Characteristic (Firmware β App): T3838386E400003-B5A3-F393-E0A9-E50E24DCCA9E
Connection Steps
1. Scan for Devices
β’ Scan for BLE devices advertising the MeshCore Service UUID
β’ Optionally filter by device name (typically contains "MeshCore" prefix)
β’ Note the device MAC address for reconnection
2. Connect to GATT
β’ Connect to the device using the discovered MAC address
β’ Wait for connection to be established
3. Discover Services and Characteristics
β’ Discover the service with UUID T3838386E400001-B5A3-F393-E0A9-E50E24DCCA9E
β’ Discover the RX characteristic T3838386E400002-B5A3-F393-E0A9-E50E24DCCA9E
β’ Your app writes to this, the firmware reads from this
β’ Discover the TX characteristic T3838386E400003-B5A3-F393-E0A9-E50E24DCCA9E
β’ The firmware writes to this, your app reads from this
4. Enable Notifications
β’ Subscribe to notifications on the TX characteristic to receive data from the firmware
5. Send Initial Commands
β’ Send T383838CMD_APP_START to identify your app to firmware and get radio settings
β’ Send T383838CMD_DEVICE_QUERY to fetch device info and negotiate supported protocol versions
β’ Send T383838CMD_SET_DEVICE_TIME to set the firmware clock
β’ Send T383838CMD_GET_CONTACTS to fetch all contacts
β’ Send T383838CMD_GET_CHANNEL multiple times to fetch all channel slots
β’ Send T383838CMD_SYNC_NEXT_MESSAGE to fetch the next message stored in firmware
β’ Setup listeners for push codes, such as T383838PUSH_CODE_MSG_WAITING or T383838PUSH_CODE_ADVERT
β’ See Commands section for information on other commands
Note: MeshCore devices may disconnect after periods of inactivity. Implement auto-reconnect logic with exponential backoff.
BLE Write Type
When writing commands to the RX characteristic, specify the write type:
β’ Write with Response (default): Waits for acknowledgment from device
β’ Write without Response: Faster but no acknowledgment
Platform-specific:
β’ Android: Use T383838BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT or T383838WRITE_TYPE_NO_RESPONSE
β’ iOS: Use T383838CBCharacteristicWriteType.withResponse or T383838.withoutResponse
β’ Python (bleak): Use T383838write_gatt_char() with T383838response=True or T383838False
Recommendation: Use write with response for reliability.
MTU (Maximum Transmission Unit)
The default BLE MTU is 23 bytes (20 bytes payload). For larger commands like T383838SET_CHANNEL (50 bytes), you may need to:
1. Request Larger MTU: Request MTU of 512 bytes if supported
β’ Android: T383838gatt.requestMtu(512)
β’ iOS: T383838peripheral.maximumWriteValueLength(for:)
β’ Python (bleak): MTU is negotiated automatically
Command Sequencing
Critical: Commands must be sent in the correct sequence:
1. After Connection:
β’ Wait for BLE connection to be established
β’ Wait for services/characteristics to be discovered
β’ Wait for notifications to be enabled
β’ Now you can safely send commands to the firmware
2. Command-Response Matching:
β’ Send one command at a time
β’ Wait for a response before sending another command
β’ Use a timeout (typically 5 seconds)
β’ Match response to command by type (e.g: T383838CMD_GET_CHANNEL β T383838RESP_CODE_CHANNEL_INFO)
Command Queue Management
For reliable operation, implement a command queue.
Queue Structure:
β’ Maintain a queue of pending commands
β’ Track which command is currently waiting for a response
β’ Only send next command after receiving response or timeout
Error Handling:
β’ On timeout, clear current command, process next in queue
β’ On error, log error, clear current command, process next
Packet Structure
The MeshCore protocol uses a binary format with the following structure:
β’ Commands: Sent from app to firmware via RX characteristic
β’ Responses: Received from firmware via TX characteristic notifications
β’ All multi-byte integers: Little-endian byte order (except CayenneLPP which is Big-endian)
β’ All strings: UTF-8 encoding
Most packets follow this format:
T282828
[Packet Type (1 byte)] [Data (variable length)]
The first byte indicates the packet type (see Response Parsing).
Commands
1. App Start
Purpose: Initialize communication with the device. Must be sent first after connection.
Command Format:
T282828
Byte 0: 0x01
Bytes 1-7: Reserved (currently ignored by firmware)
Bytes 8+: Application name (UTF-8, optional)
Example (hex):
T282828
01 00 00 00 00 00 00 00 6d 63 63 6c 69
Response: T383838PACKET_SELF_INFO (0x05)
2. Device Query
Purpose: Query device information.
Command Format:
T282828
Byte 0: 0x16
Byte 1: 0x03
Example (hex):
T282828
16 03
Response: T383838PACKET_DEVICE_INFO (0x0D) with device information
3. Get Channel Info
Purpose: Retrieve information about a specific channel.
Command Format:
T282828
Byte 0: 0x1F
Byte 1: Channel Index (0-7)
Example (get channel 1):
T282828
1F 01
Response: T383838PACKET_CHANNEL_INFO (0x12) with channel details
4. Set Channel
Purpose: Create or update a channel on the device.
Command Format:
T282828
Byte 0: 0x20
Byte 1: Channel Index (0-7)
Bytes 2-33: Channel Name (32 bytes, UTF-8, null-padded)
Bytes 34-49: Secret (16 bytes)
Total Length: 50 bytes
Channel Index:
β’ Index 0: Reserved for public channels (no secret)
β’ Indices 1-7: Available for private channels
Channel Name:
β’ UTF-8 encoded
β’ Maximum 32 bytes
β’ Padded with null bytes (0x00) if shorter
Secret Field (16 bytes):
β’ For private channels: 16-byte secret
β’ For public channels: All zeros (0x00)
Example (create channel "YourChannelName" at index 1 with secret):
T282828
20 01 53 4D 53 00 00 ... (name padded to 32 bytes)
[16 bytes of secret]
Note: The 32-byte secret variant is unsupported and returns T383838PACKET_ERROR.
Response: T383838PACKET_OK (0x00) on success, T383838PACKET_ERROR (0x01) on failure
5. Send Channel Message
Purpose: Send a text message to a channel.
Command Format:
T282828
Byte 0: 0x03
Byte 1: 0x00
Byte 2: Channel Index (0-7)
Bytes 3-6: Timestamp (32-bit little-endian Unix timestamp, seconds)
Bytes 7+: Message Text (UTF-8, variable length)
Timestamp: Unix timestamp in seconds (32-bit unsigned integer, little-endian)
Example (send "Hello" to channel 1 at timestamp 1234567890):
T282828
03 00 01 D2 02 96 49 48 65 6C 6C 6F
Response: T383838PACKET_MSG_SENT (0x06) on success
6. Send Channel Data Datagram
Purpose: Send a binary datagram to a channel. Unlike channel text messages, datagrams carry no built-in sender identity and no timestamp β applications needing either must encode them inside the binary payload.
Command Format:
T282828
Byte 0: 0x3E
Byte 1: Channel Index (0-7)
Byte 2: Path Length (0xFF = flood, otherwise actual path length)
Bytes 3 .. 2+path_len: Path (omitted when path_len == 0xFF)
Next 2 bytes (little-endian): Data Type (\`data_type\`, uint16)
Remaining bytes: Binary payload (variable length)
Example (flood, T383838DATA_TYPE_DEV, payload T383838A1 B2 C3, channel 1):
T282828
3E 01 FF FF FF A1 B2 C3
Data Type / Transport Mapping:
β’ T3838380x0000 (T383838DATA_TYPE_RESERVED) is invalid and rejected with T383838PACKET_ERROR.
β’ T3838380xFFFF (T383838DATA_TYPE_DEV) is the developer namespace for experimenting and developing apps.
β’ Values T3838380x0001βT3838380xFFFE are available for registered application/community namespaces. See the Registered data_type values table below.
Limits:
β’ Maximum payload length is T383838MAX_CHANNEL_DATA_LENGTH = MAX_FRAME_SIZE - 9 = 163 bytes.
β’ Larger payloads are rejected with T383838PACKET_ERROR (T383838ERR_CODE_ILLEGAL_ARG).
Response: T383838PACKET_OK (0x00) on success, or T383838PACKET_ERROR (0x01) with one of:
β’ T383838ERR_CODE_NOT_FOUND (2) β unknown T383838channel_idx
β’ T383838ERR_CODE_ILLEGAL_ARG (6) β invalid T383838path_len, reserved T383838data_type (T3838380x0000), or payload larger than T383838MAX_CHANNEL_DATA_LENGTH
β’ T383838ERR_CODE_TABLE_FULL (3) β outbound send queue is full; retry later
Inbound datagrams are delivered to the host via T383838RESP_CODE_CHANNEL_DATA_RECV (0x1B); see Receive Channel Data Datagram.
Registered T383838data_type values
T383838data_type is an application identifier, not a payload-format identifier. Each registered value identifies an application that owns its own internal payload schemas. The firmware does not inspect payload contents β T383838data_type is transported opaquely.
βββββββββββββββββββ¬βββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Value β Constant β Purpose β
βββββββββββββββββββΌβββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β 0x0000 β T383838DATA_TYPE_RESERVED β Reserved; invalid on send β
β 0x0001 β 0x00FF β β β Reserved for internal use β
β 0x0100 β 0xFEFF β β β Registered application namespaces (see number_allocati. Internal sub-formats within an allocated application ID are owned by that application and are not tracked in MeshCore firmware or this document.
Receive Channel Data Datagram
Inbound group datagrams (radio-level T383838PAYLOAD_TYPE_GRP_DATA, 0x06) are forwarded to the host as T383838RESP_CODE_CHANNEL_DATA_RECV notifications.
Frame Format (T383838RESP_CODE_CHANNEL_DATA_RECV, 0x1B):
T282828
Byte 0: 0x1B (packet type)
Byte 1: SNR (signed int8, scaled Γ4 β divide by 4.0 to recover dB)
Bytes 2-3: Reserved (clients MUST ignore)
Byte 4: Channel Index (0-7)
Byte 5: Path Length (actual path length when flooded, otherwise 0xFF for direct)
Bytes 6-7: Data Type (uint16 little-endian)
Byte 8: Data Length
Bytes 9 .. 8+data_len: Payload
Path bytes are not forwarded: Only T383838path_len is reported in the receive frame β the path itself is not copied to the host. There are no path bytes between byte 5 and the data_type field at bytes 6β7, regardless of T383838path_len.
Path Length semantics differ between send and receive:
βββββββββββββ¬ββββββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Direction β `BT383838`Fdddpath_len = 0xFF`f`b β `BT383838`Fdddpath_len β 0xFF`f`b β
βββββββββββββΌββββββββββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β Send β Flood the network β Direct route; the encoded path follows (low 6 bitβ¦ β
β Receive β Packet arrived via direct route β Packet was flooded; this is the encoded T383838pkt->pathβ¦ β
βββββββββββββ΄ββββββββββββββββββββββββββββββββββ΄βββββββββββββββββββββββββββββββββββββββββββββββββββββ
In other words, the meaning of T3838380xFF is inverted between the two directions, and on receive the field carries metadata only β never a routable path. T383838path_len is an encoded byte (see T383838Packet::isValidPathLen / T383838Packet::writePath in T383838src/Packet.cpp), not a raw byte count.
Note: The device may also emit T383838PACKET_MESSAGES_WAITING (0x83) to notify the host that datagrams are queued; poll with T383838CMD_SYNC_NEXT_MESSAGE (0x0A) to retrieve them.
Parsing Pseudocode:
T282828
Tff7b72def Td2a8ffparse_channel_data_recvTb4b4b4(Te6edf3dataTb4b4b4)Tb4b4b4:
Tff7b72if Tffa657lenTb4b4b4(Te6edf3dataTb4b4b4) Tff7b72< T79c0ff9Tb4b4b4:
Tff7b72return Tff7b72None
Te6edf3snr_byte Tff7b72= Te6edf3dataTb4b4b4[T79c0ff1Tb4b4b4]
Te6edf3snr Tff7b72= Tb4b4b4(Te6edf3snr_byte Tff7b72if Te6edf3snr_byte Tff7b72< T79c0ff128 Tff7b72else Te6edf3snr_byte Tff7b72- T79c0ff256Tb4b4b4) Tff7b72/ T79c0ff4.0
Te6edf3channel_idx Tff7b72= Te6edf3dataTb4b4b4[T79c0ff4Tb4b4b4]
Te6edf3path_len Tff7b72= Te6edf3dataTb4b4b4[T79c0ff5Tb4b4b4]
Te6edf3data_type Tff7b72= Tffa657intTff7b72.Td2a8fffrom_bytesTb4b4b4(Te6edf3dataTb4b4b4[T79c0ff6Tb4b4b4:T79c0ff8Tb4b4b4]Tb4b4b4, Ta5d6ff'Ta5d6fflittleTa5d6ff'Tb4b4b4)
Te6edf3data_len Tff7b72= Te6edf3dataTb4b4b4[T79c0ff8Tb4b4b4]
Tff7b72if T79c0ff9 Tff7b72+ Te6edf3data_len Tff7b72> Tffa657lenTb4b4b4(Te6edf3dataTb4b4b4)Tb4b4b4:
Tff7b72return Tff7b72None
Te6edf3payload Tff7b72= Te6edf3dataTb4b4b4[T79c0ff9Tb4b4b4:T79c0ff9 Tff7b72+ Te6edf3data_lenTb4b4b4]
Tff7b72return Tb4b4b4{
Ta5d6ff'Ta5d6ffsnrTa5d6ff'Tb4b4b4: Te6edf3snrTb4b4b4,
Ta5d6ff'Ta5d6ffchannel_idxTa5d6ff'Tb4b4b4: Te6edf3channel_idxTb4b4b4,
Ta5d6ff'Ta5d6ffpath_lenTa5d6ff'Tb4b4b4: Te6edf3path_lenTb4b4b4,
Ta5d6ff'Ta5d6ffdata_typeTa5d6ff'Tb4b4b4: Te6edf3data_typeTb4b4b4,
Ta5d6ff'Ta5d6ffpayloadTa5d6ff'Tb4b4b4: Tffa657bytesTb4b4b4(Te6edf3payloadTb4b4b4)Tb4b4b4,
Tb4b4b4}
7. Get Message
Purpose: Request the next queued message from the device.
Command Format:
T282828
Byte 0: 0x0A
Example (hex):
T282828
0A
Response:
β’ T383838PACKET_CHANNEL_MSG_RECV (0x08) or T383838PACKET_CHANNEL_MSG_RECV_V3 (0x11) for channel messages
β’ T383838PACKET_CONTACT_MSG_RECV (0x07) or T383838PACKET_CONTACT_MSG_RECV_V3 (0x10) for contact messages
β’ T383838PACKET_CHANNEL_DATA_RECV (0x1B) for channel data datagrams
β’ T383838PACKET_NO_MORE_MSGS (0x0A) if no messages available
Note: Poll this command periodically to retrieve queued messages. The device may also send T383838PACKET_MESSAGES_WAITING (0x83) as a notification when messages are available.
8. Get Battery and Storage
Purpose: Query device battery voltage and storage usage.
Command Format:
T282828
Byte 0: 0x14
Example (hex):
T282828
14
Response: T383838PACKET_BATTERY (0x0C) with battery millivolts and storage information
Channel Management
Channel Types
1. Public Channel
β’ Uses a publicly known 16-byte key: T3838388b3387e9c5cdea6ac9e5edbaa115cd72
β’ Anyone can join this channel, messages should be considered public
β’ Used as the default public group chat
2. Hashtag Channels
β’ Uses a secret key derived from the channel name
β’ It is the first 16 bytes of T383838sha256("#test")
β’ For example hashtag channel T383838#test has the key: T3838389cd8fcf22a47333b591d96a2b848b73f
β’ Used as a topic based public group chat, separate from the default public channel
3. Private Channels
β’ Uses a randomly generated 16-byte secret key
β’ Messages should be considered private between those that know the secret
β’ Users should keep the key secret, and only share with those you want to communicate with
β’ Used as a secure private group chat
Channel Lifecycle
1. Set Channel:
β’ Fetch all channel slots, and find one with empty name and all-zero secret
β’ Generate or provide a 16-byte secret
β’ Send T383838CMD_SET_CHANNEL with name and a 16-byte secret
2. Get Channel:
β’ Send T383838CMD_GET_CHANNEL with channel index
β’ Parse T383838RESP_CODE_CHANNEL_INFO response
3. Delete Channel:
β’ Send T383838CMD_SET_CHANNEL with empty name and all-zero secret
β’ Or overwrite with a new channel
Message Handling
Receiving Messages
Messages are received via the TX characteristic (notifications). The device sends:
1. Channel Messages:
β’ T383838PACKET_CHANNEL_MSG_RECV (0x08) - Standard format
β’ T383838PACKET_CHANNEL_MSG_RECV_V3 (0x11) - Version 3 with SNR
2. Contact Messages:
β’ T383838PACKET_CONTACT_MSG_RECV (0x07) - Standard format
β’ T383838PACKET_CONTACT_MSG_RECV_V3 (0x10) - Version 3 with SNR
3. Notifications:
β’ T383838PACKET_MESSAGES_WAITING (0x83) - Indicates messages are queued
Contact Message Format
Standard Format (T383838PACKET_CONTACT_MSG_RECV, 0x07):
T282828
Byte 0: 0x07 (packet type)
Bytes 1-6: Public Key Prefix (6 bytes, hex)
Byte 7: Path Length
Byte 8: Text Type
Bytes 9-12: Timestamp (32-bit little-endian)
Bytes 13-16: Signature (4 bytes, only if txt_type == 2)
Bytes 17+: Message Text (UTF-8)
V3 Format (T383838PACKET_CONTACT_MSG_RECV_V3, 0x10):
T282828
Byte 0: 0x10 (packet type)
Byte 1: SNR (signed byte, multiplied by 4)
Bytes 2-3: Reserved
Bytes 4-9: Public Key Prefix (6 bytes, hex)
Byte 10: Path Length
Byte 11: Text Type
Bytes 12-15: Timestamp (32-bit little-endian)
Bytes 16-19: Signature (4 bytes, only if txt_type == 2)
Bytes 20+: Message Text (UTF-8)
Parsing Pseudocode:
T282828
Tff7b72def Td2a8ffparse_contact_messageTb4b4b4(Te6edf3dataTb4b4b4)Tb4b4b4:
Te6edf3packet_type Tff7b72= Te6edf3dataTb4b4b4[T79c0ff0Tb4b4b4]
Te6edf3offset Tff7b72= T79c0ff1
T8b949e# Check for V3 format
Tff7b72if Te6edf3packet_type Tff7b72== T79c0ff0x10Tb4b4b4: T8b949e# V3
Te6edf3snr_byte Tff7b72= Te6edf3dataTb4b4b4[Te6edf3offsetTb4b4b4]
Te6edf3snr Tff7b72= Tb4b4b4(Tb4b4b4(Te6edf3snr_byte Tff7b72if Te6edf3snr_byte Tff7b72< T79c0ff128 Tff7b72else Te6edf3snr_byte Tff7b72- T79c0ff256Tb4b4b4) Tff7b72/ T79c0ff4.0Tb4b4b4)
Te6edf3offset Tff7b72+Tff7b72= T79c0ff3 T8b949e# Skip SNR + reserved
Te6edf3pubkey_prefix Tff7b72= Te6edf3dataTb4b4b4[Te6edf3offsetTb4b4b4:Te6edf3offsetTff7b72+T79c0ff6Tb4b4b4]Tff7b72.Td2a8ffhexTb4b4b4(Tb4b4b4)
Te6edf3offset Tff7b72+Tff7b72= T79c0ff6
Te6edf3path_len Tff7b72= Te6edf3dataTb4b4b4[Te6edf3offsetTb4b4b4]
Te6edf3txt_type Tff7b72= Te6edf3dataTb4b4b4[Te6edf3offset Tff7b72+ T79c0ff1Tb4b4b4]
Te6edf3offset Tff7b72+Tff7b72= T79c0ff2
Te6edf3timestamp Tff7b72= Tffa657intTff7b72.Td2a8fffrom_bytesTb4b4b4(Te6edf3dataTb4b4b4[Te6edf3offsetTb4b4b4:Te6edf3offsetTff7b72+T79c0ff4Tb4b4b4]Tb4b4b4, Ta5d6ff'Ta5d6fflittleTa5d6ff'Tb4b4b4)
Te6edf3offset Tff7b72+Tff7b72= T79c0ff4
T8b949e# If txt_type == 2, skip 4-byte signature
Tff7b72if Te6edf3txt_type Tff7b72== T79c0ff2Tb4b4b4:
Te6edf3offset Tff7b72+Tff7b72= T79c0ff4
Te6edf3message Tff7b72= Te6edf3dataTb4b4b4[Te6edf3offsetTb4b4b4:Tb4b4b4]Tff7b72.Td2a8ffdecodeTb4b4b4(Ta5d6ff'Ta5d6ffutf-8Ta5d6ff'Tb4b4b4)
Tff7b72return Tb4b4b4{
Ta5d6ff'Ta5d6ffpubkey_prefixTa5d6ff'Tb4b4b4: Te6edf3pubkey_prefixTb4b4b4,
Ta5d6ff'Ta5d6ffpath_lenTa5d6ff'Tb4b4b4: Te6edf3path_lenTb4b4b4,
Ta5d6ff'Ta5d6fftxt_typeTa5d6ff'Tb4b4b4: Te6edf3txt_typeTb4b4b4,
Ta5d6ff'Ta5d6fftimestampTa5d6ff'Tb4b4b4: Te6edf3timestampTb4b4b4,
Ta5d6ff'Ta5d6ffmessageTa5d6ff'Tb4b4b4: Te6edf3messageTb4b4b4,
Ta5d6ff'Ta5d6ffsnrTa5d6ff'Tb4b4b4: Te6edf3snr Tff7b72if Te6edf3packet_type Tff7b72== T79c0ff0x10 Tff7b72else Tff7b72None
Tb4b4b4}
Channel Message Format
Standard Format (T383838PACKET_CHANNEL_MSG_RECV, 0x08):
T282828
Byte 0: 0x08 (packet type)
Byte 1: Channel Index (0-7)
Byte 2: Path Length
Byte 3: Text Type
Bytes 4-7: Timestamp (32-bit little-endian)
Bytes 8+: Message Text (UTF-8)
V3 Format (T383838PACKET_CHANNEL_MSG_RECV_V3, 0x11):
T282828
Byte 0: 0x11 (packet type)
Byte 1: SNR (signed byte, multiplied by 4)
Bytes 2-3: Reserved
Byte 4: Channel Index (0-7)
Byte 5: Path Length
Byte 6: Text Type
Bytes 7-10: Timestamp (32-bit little-endian)
Bytes 11+: Message Text (UTF-8)
Parsing Pseudocode:
T282828
Tff7b72def Td2a8ffparse_channel_messageTb4b4b4(Te6edf3dataTb4b4b4)Tb4b4b4:
Te6edf3packet_type Tff7b72= Te6edf3dataTb4b4b4[T79c0ff0Tb4b4b4]
Te6edf3offset Tff7b72= T79c0ff1
T8b949e# Check for V3 format
Tff7b72if Te6edf3packet_type Tff7b72== T79c0ff0x11Tb4b4b4: T8b949e# V3
Te6edf3snr_byte Tff7b72= Te6edf3dataTb4b4b4[Te6edf3offsetTb4b4b4]
Te6edf3snr Tff7b72= Tb4b4b4(Tb4b4b4(Te6edf3snr_byte Tff7b72if Te6edf3snr_byte Tff7b72< T79c0ff128 Tff7b72else Te6edf3snr_byte Tff7b72- T79c0ff256Tb4b4b4) Tff7b72/ T79c0ff4.0Tb4b4b4)
Te6edf3offset Tff7b72+Tff7b72= T79c0ff3 T8b949e# Skip SNR + reserved
Te6edf3channel_idx Tff7b72= Te6edf3dataTb4b4b4[Te6edf3offsetTb4b4b4]
Te6edf3path_len Tff7b72= Te6edf3dataTb4b4b4[Te6edf3offset Tff7b72+ T79c0ff1Tb4b4b4]
Te6edf3txt_type Tff7b72= Te6edf3dataTb4b4b4[Te6edf3offset Tff7b72+ T79c0ff2Tb4b4b4]
Te6edf3timestamp Tff7b72= Tffa657intTff7b72.Td2a8fffrom_bytesTb4b4b4(Te6edf3dataTb4b4b4[Te6edf3offsetTff7b72+T79c0ff3Tb4b4b4:Te6edf3offsetTff7b72+T79c0ff7Tb4b4b4]Tb4b4b4, Ta5d6ff'Ta5d6fflittleTa5d6ff'Tb4b4b4)
Te6edf3message Tff7b72= Te6edf3dataTb4b4b4[Te6edf3offsetTff7b72+T79c0ff7Tb4b4b4:Tb4b4b4]Tff7b72.Td2a8ffdecodeTb4b4b4(Ta5d6ff'Ta5d6ffutf-8Ta5d6ff'Tb4b4b4)
Tff7b72return Tb4b4b4{
Ta5d6ff'Ta5d6ffchannel_idxTa5d6ff'Tb4b4b4: Te6edf3channel_idxTb4b4b4,
Ta5d6ff'Ta5d6fftimestampTa5d6ff'Tb4b4b4: Te6edf3timestampTb4b4b4,
Ta5d6ff'Ta5d6ffmessageTa5d6ff'Tb4b4b4: Te6edf3messageTb4b4b4,
Ta5d6ff'Ta5d6ffsnrTa5d6ff'Tb4b4b4: Te6edf3snr Tff7b72if Te6edf3packet_type Tff7b72== T79c0ff0x11 Tff7b72else Tff7b72None
Tb4b4b4}
Sending Messages
Use the T383838SEND_CHANNEL_MESSAGE command (see Commands).
Important:
β’ Messages are limited to 133 characters per MeshCore specification
β’ Long messages should be split into chunks
β’ Include a chunk indicator (e.g., "[1/3] message text")
Response Parsing
Terminology
This document uses a spec-level naming convention (T383838PACKET_*) for bytes the firmware sends back to the host. In the firmware source these same values are split across two T383838#define families by purpose:
β’ T383838RESP_CODE_* β direct replies to a command (e.g. T383838RESP_CODE_CHANNEL_DATA_RECV = T383838PACKET_CHANNEL_DATA_RECV = 0x1B).
β’ T383838PUSH_CODE_* β asynchronous notifications not tied to a specific command (e.g. T383838PUSH_CODE_MSG_WAITING = T383838PACKET_MESSAGES_WAITING = 0x83).
Byte values are authoritative; names are aliases. When reading firmware source, T383838RESP_CODE_X / T383838PUSH_CODE_X correspond to this doc's T383838PACKET_X of the same numeric value.
Packet Types
βββββββββ¬βββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββ
β Value β Name β Description β
βββββββββΌβββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββ€
β 0x00 β PACKET_OK β Command succeeded β
β 0x01 β PACKET_ERROR β Command failed β
β 0x02 β PACKETCONTACTSTART β Start of contact list β
β 0x03 β PACKET_CONTACT β Contact information β
β 0x04 β PACKETCONTACTEND β End of contact list β
β 0x05 β PACKETSELFINFO β Device self-information β
β 0x06 β PACKETMSGSENT β Message sent confirmation β
β 0x07 β PACKETCONTACTMSG_RECV β Contact message (standard) β
β 0x08 β PACKETCHANNELMSG_RECV β Channel message (standard) β
β 0x09 β PACKETCURRENTTIME β Current time response β
β 0x0A β PACKETNOMORE_MSGS β No more messages available β
β 0x0C β PACKET_BATTERY β Battery level β
β 0x0D β PACKETDEVICEINFO β Device information β
β 0x10 β PACKETCONTACTMSGRECVV3 β Contact message (V3 with SNR) β
β 0x11 β PACKETCHANNELMSGRECVV3 β Channel message (V3 with SNR) β
β 0x12 β PACKETCHANNELINFO β Channel information β
β 0x1B β PACKETCHANNELDATA_RECV β Channel data datagram β
β 0x80 β PACKET_ADVERTISEMENT β Advertisement packet β
β 0x82 β PACKET_ACK β Acknowledgment β
β 0x83 β PACKETMESSAGESWAITING β Messages waiting notification β
β 0x88 β PACKETLOGDATA β RF log data (can be ignored) β
βββββββββ΄βββββββββββββββββββββββββ΄ββββββββββββββββββββββββββββββββ
Parsing Responses
PACKET_OK (0x00):
T282828
Byte 0: 0x00
Bytes 1-4: Optional value (32-bit little-endian integer)
PACKET_ERROR (0x01):
T282828
Byte 0: 0x01
Byte 1: Error code (optional)
PACKETCHANNELINFO (0x12):
T282828
Byte 0: 0x12
Byte 1: Channel Index
Bytes 2-33: Channel Name (32 bytes, null-terminated)
Bytes 34-49: Secret (16 bytes)
Note: The device returns the 16-byte channel secret in this response.
PACKETDEVICEINFO (0x0D):
T282828
Byte 0: 0x0D
Byte 1: Firmware Version (uint8)
Bytes 2+: Variable length based on firmware version
For firmware version >= 3:
Byte 2: Max Contacts Raw (uint8, actual = value * 2)
Byte 3: Max Channels (uint8)
Bytes 4-7: BLE PIN (32-bit little-endian)
Bytes 8-19: Firmware Build (12 bytes, UTF-8, null-padded)
Bytes 20-59: Model (40 bytes, UTF-8, null-padded)
Bytes 60-79: Version (20 bytes, UTF-8, null-padded)
Byte 80: Client repeat enabled/preferred (firmware v9+)
Byte 81: Path hash mode (firmware v10+)
Parsing Pseudocode:
T282828
Tff7b72def Td2a8ffparse_device_infoTb4b4b4(Te6edf3dataTb4b4b4)Tb4b4b4:
Tff7b72if Tffa657lenTb4b4b4(Te6edf3dataTb4b4b4) Tff7b72< T79c0ff2Tb4b4b4:
Tff7b72return Tff7b72None
Te6edf3fw_ver Tff7b72= Te6edf3dataTb4b4b4[T79c0ff1Tb4b4b4]
Te6edf3info Tff7b72= Tb4b4b4{Ta5d6ff'Ta5d6fffw_verTa5d6ff'Tb4b4b4: Te6edf3fw_verTb4b4b4}
Tff7b72if Te6edf3fw_ver Tff7b72>Tff7b72= T79c0ff3 Tff7b72and Tffa657lenTb4b4b4(Te6edf3dataTb4b4b4) Tff7b72>Tff7b72= T79c0ff80Tb4b4b4:
Te6edf3infoTb4b4b4[Ta5d6ff'Ta5d6ffmax_contactsTa5d6ff'Tb4b4b4] Tff7b72= Te6edf3dataTb4b4b4[T79c0ff2Tb4b4b4] Tff7b72* T79c0ff2
Te6edf3infoTb4b4b4[Ta5d6ff'Ta5d6ffmax_channelsTa5d6ff'Tb4b4b4] Tff7b72= Te6edf3dataTb4b4b4[T79c0ff3Tb4b4b4]
Te6edf3infoTb4b4b4[Ta5d6ff'Ta5d6ffble_pinTa5d6ff'Tb4b4b4] Tff7b72= Tffa657intTff7b72.Td2a8fffrom_bytesTb4b4b4(Te6edf3dataTb4b4b4[T79c0ff4Tb4b4b4:T79c0ff8Tb4b4b4]Tb4b4b4, Ta5d6ff'Ta5d6fflittleTa5d6ff'Tb4b4b4)
Te6edf3infoTb4b4b4[Ta5d6ff'Ta5d6fffw_buildTa5d6ff'Tb4b4b4] Tff7b72= Te6edf3dataTb4b4b4[T79c0ff8Tb4b4b4:T79c0ff20Tb4b4b4]Tff7b72.Td2a8ffdecodeTb4b4b4(Ta5d6ff'Ta5d6ffutf-8Ta5d6ff'Tb4b4b4)Tff7b72.Td2a8ffrstripTb4b4b4(Ta5d6ff'Tffea00\x00Ta5d6ff'Tb4b4b4)Tff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4)
Te6edf3infoTb4b4b4[Ta5d6ff'Ta5d6ffmodelTa5d6ff'Tb4b4b4] Tff7b72= Te6edf3dataTb4b4b4[T79c0ff20Tb4b4b4:T79c0ff60Tb4b4b4]Tff7b72.Td2a8ffdecodeTb4b4b4(Ta5d6ff'Ta5d6ffutf-8Ta5d6ff'Tb4b4b4)Tff7b72.Td2a8ffrstripTb4b4b4(Ta5d6ff'Tffea00\x00Ta5d6ff'Tb4b4b4)Tff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4)
Te6edf3infoTb4b4b4[Ta5d6ff'Ta5d6ffverTa5d6ff'Tb4b4b4] Tff7b72= Te6edf3dataTb4b4b4[T79c0ff60Tb4b4b4:T79c0ff80Tb4b4b4]Tff7b72.Td2a8ffdecodeTb4b4b4(Ta5d6ff'Ta5d6ffutf-8Ta5d6ff'Tb4b4b4)Tff7b72.Td2a8ffrstripTb4b4b4(Ta5d6ff'Tffea00\x00Ta5d6ff'Tb4b4b4)Tff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4)
Tff7b72return Te6edf3info
PACKET_BATTERY (0x0C):
T282828
Byte 0: 0x0C
Bytes 1-2: Battery Voltage (16-bit little-endian, millivolts)
Bytes 3-6: Used Storage (32-bit little-endian, KB)
Bytes 7-10: Total Storage (32-bit little-endian, KB)
Parsing Pseudocode:
T282828
Tff7b72def Td2a8ffparse_batteryTb4b4b4(Te6edf3dataTb4b4b4)Tb4b4b4:
Tff7b72if Tffa657lenTb4b4b4(Te6edf3dataTb4b4b4) Tff7b72< T79c0ff3Tb4b4b4:
Tff7b72return Tff7b72None
Te6edf3mv Tff7b72= Tffa657intTff7b72.Td2a8fffrom_bytesTb4b4b4(Te6edf3dataTb4b4b4[T79c0ff1Tb4b4b4:T79c0ff3Tb4b4b4]Tb4b4b4, Ta5d6ff'Ta5d6fflittleTa5d6ff'Tb4b4b4)
Te6edf3info Tff7b72= Tb4b4b4{Ta5d6ff'Ta5d6ffbattery_mvTa5d6ff'Tb4b4b4: Te6edf3mvTb4b4b4}
Tff7b72if Tffa657lenTb4b4b4(Te6edf3dataTb4b4b4) Tff7b72>Tff7b72= T79c0ff11Tb4b4b4:
Te6edf3infoTb4b4b4[Ta5d6ff'Ta5d6ffused_kbTa5d6ff'Tb4b4b4] Tff7b72= Tffa657intTff7b72.Td2a8fffrom_bytesTb4b4b4(Te6edf3dataTb4b4b4[T79c0ff3Tb4b4b4:T79c0ff7Tb4b4b4]Tb4b4b4, Ta5d6ff'Ta5d6fflittleTa5d6ff'Tb4b4b4)
Te6edf3infoTb4b4b4[Ta5d6ff'Ta5d6fftotal_kbTa5d6ff'Tb4b4b4] Tff7b72= Tffa657intTff7b72.Td2a8fffrom_bytesTb4b4b4(Te6edf3dataTb4b4b4[T79c0ff7Tb4b4b4:T79c0ff11Tb4b4b4]Tb4b4b4, Ta5d6ff'Ta5d6fflittleTa5d6ff'Tb4b4b4)
Tff7b72return Te6edf3info
PACKETSELFINFO (0x05):
T282828
Byte 0: 0x05
Byte 1: Advertisement Type
Byte 2: TX Power
Byte 3: Max TX Power
Bytes 4-35: Public Key (32 bytes, hex)
Bytes 36-39: Advertisement Latitude (32-bit little-endian, divided by 1e6)
Bytes 40-43: Advertisement Longitude (32-bit little-endian, divided by 1e6)
Byte 44: Multi ACKs
Byte 45: Advertisement Location Policy
Byte 46: Telemetry Mode (bitfield)
Byte 47: Manual Add Contacts (bool)
Bytes 48-51: Radio Frequency (32-bit little-endian, divided by 1000.0)
Bytes 52-55: Radio Bandwidth (32-bit little-endian, divided by 1000.0)
Byte 56: Radio Spreading Factor
Byte 57: Radio Coding Rate
Bytes 58+: Device Name (UTF-8, variable length, no null terminator required)
Parsing Pseudocode:
T282828
Tff7b72def Td2a8ffparse_self_infoTb4b4b4(Te6edf3dataTb4b4b4)Tb4b4b4:
Tff7b72if Tffa657lenTb4b4b4(Te6edf3dataTb4b4b4) Tff7b72< T79c0ff36Tb4b4b4:
Tff7b72return Tff7b72None
Te6edf3offset Tff7b72= T79c0ff1
Te6edf3info Tff7b72= Tb4b4b4{
Ta5d6ff'Ta5d6ffadv_typeTa5d6ff'Tb4b4b4: Te6edf3dataTb4b4b4[Te6edf3offsetTb4b4b4]Tb4b4b4,
Ta5d6ff'Ta5d6fftx_powerTa5d6ff'Tb4b4b4: Te6edf3dataTb4b4b4[Te6edf3offset Tff7b72+ T79c0ff1Tb4b4b4]Tb4b4b4,
Ta5d6ff'Ta5d6ffmax_tx_powerTa5d6ff'Tb4b4b4: Te6edf3dataTb4b4b4[Te6edf3offset Tff7b72+ T79c0ff2Tb4b4b4]Tb4b4b4,
Ta5d6ff'Ta5d6ffpublic_keyTa5d6ff'Tb4b4b4: Te6edf3dataTb4b4b4[Te6edf3offset Tff7b72+ T79c0ff3Tb4b4b4:Te6edf3offset Tff7b72+ T79c0ff35Tb4b4b4]Tff7b72.Td2a8ffhexTb4b4b4(Tb4b4b4)
Tb4b4b4}
Te6edf3offset Tff7b72+Tff7b72= T79c0ff35
Te6edf3lat Tff7b72= Tffa657intTff7b72.Td2a8fffrom_bytesTb4b4b4(Te6edf3dataTb4b4b4[Te6edf3offsetTb4b4b4:Te6edf3offsetTff7b72+T79c0ff4Tb4b4b4]Tb4b4b4, Ta5d6ff'Ta5d6fflittleTa5d6ff'Tb4b4b4) Tff7b72/ T79c0ff1e6
Te6edf3lon Tff7b72= Tffa657intTff7b72.Td2a8fffrom_bytesTb4b4b4(Te6edf3dataTb4b4b4[Te6edf3offsetTff7b72+T79c0ff4Tb4b4b4:Te6edf3offsetTff7b72+T79c0ff8Tb4b4b4]Tb4b4b4, Ta5d6ff'Ta5d6fflittleTa5d6ff'Tb4b4b4) Tff7b72/ T79c0ff1e6
Te6edf3infoTb4b4b4[Ta5d6ff'Ta5d6ffadv_latTa5d6ff'Tb4b4b4] Tff7b72= Te6edf3lat
Te6edf3infoTb4b4b4[Ta5d6ff'Ta5d6ffadv_lonTa5d6ff'Tb4b4b4] Tff7b72= Te6edf3lon
Te6edf3offset Tff7b72+Tff7b72= T79c0ff8
Te6edf3infoTb4b4b4[Ta5d6ff'Ta5d6ffmulti_acksTa5d6ff'Tb4b4b4] Tff7b72= Te6edf3dataTb4b4b4[Te6edf3offsetTb4b4b4]
Te6edf3infoTb4b4b4[Ta5d6ff'Ta5d6ffadv_loc_policyTa5d6ff'Tb4b4b4] Tff7b72= Te6edf3dataTb4b4b4[Te6edf3offset Tff7b72+ T79c0ff1Tb4b4b4]
Te6edf3telemetry_mode Tff7b72= Te6edf3dataTb4b4b4[Te6edf3offset Tff7b72+ T79c0ff2Tb4b4b4]
Te6edf3infoTb4b4b4[Ta5d6ff'Ta5d6fftelemetry_mode_envTa5d6ff'Tb4b4b4] Tff7b72= Tb4b4b4(Te6edf3telemetry_mode Tff7b72>> T79c0ff4Tb4b4b4) Tff7b72& T79c0ff0b11
Te6edf3infoTb4b4b4[Ta5d6ff'Ta5d6fftelemetry_mode_locTa5d6ff'Tb4b4b4] Tff7b72= Tb4b4b4(Te6edf3telemetry_mode Tff7b72>> T79c0ff2Tb4b4b4) Tff7b72& T79c0ff0b11
Te6edf3infoTb4b4b4[Ta5d6ff'Ta5d6fftelemetry_mode_baseTa5d6ff'Tb4b4b4] Tff7b72= Te6edf3telemetry_mode Tff7b72& T79c0ff0b11
Te6edf3infoTb4b4b4[Ta5d6ff'Ta5d6ffmanual_add_contactsTa5d6ff'Tb4b4b4] Tff7b72= Te6edf3dataTb4b4b4[Te6edf3offset Tff7b72+ T79c0ff3Tb4b4b4] Tff7b72> T79c0ff0
Te6edf3offset Tff7b72+Tff7b72= T79c0ff4
Te6edf3freq Tff7b72= Tffa657intTff7b72.Td2a8fffrom_bytesTb4b4b4(Te6edf3dataTb4b4b4[Te6edf3offsetTb4b4b4:Te6edf3offsetTff7b72+T79c0ff4Tb4b4b4]Tb4b4b4, Ta5d6ff'Ta5d6fflittleTa5d6ff'Tb4b4b4) Tff7b72/ T79c0ff1000.0
Te6edf3bw Tff7b72= Tffa657intTff7b72.Td2a8fffrom_bytesTb4b4b4(Te6edf3dataTb4b4b4[Te6edf3offsetTff7b72+T79c0ff4Tb4b4b4:Te6edf3offsetTff7b72+T79c0ff8Tb4b4b4]Tb4b4b4, Ta5d6ff'Ta5d6fflittleTa5d6ff'Tb4b4b4) Tff7b72/ T79c0ff1000.0
Te6edf3infoTb4b4b4[Ta5d6ff'Ta5d6ffradio_freqTa5d6ff'Tb4b4b4] Tff7b72= Te6edf3freq
Te6edf3infoTb4b4b4[Ta5d6ff'Ta5d6ffradio_bwTa5d6ff'Tb4b4b4] Tff7b72= Te6edf3bw
Te6edf3infoTb4b4b4[Ta5d6ff'Ta5d6ffradio_sfTa5d6ff'Tb4b4b4] Tff7b72= Te6edf3dataTb4b4b4[Te6edf3offset Tff7b72+ T79c0ff8Tb4b4b4]
Te6edf3infoTb4b4b4[Ta5d6ff'Ta5d6ffradio_crTa5d6ff'Tb4b4b4] Tff7b72= Te6edf3dataTb4b4b4[Te6edf3offset Tff7b72+ T79c0ff9Tb4b4b4]
Te6edf3offset Tff7b72+Tff7b72= T79c0ff10
Tff7b72if Te6edf3offset Tff7b72< Tffa657lenTb4b4b4(Te6edf3dataTb4b4b4)Tb4b4b4:
Te6edf3name_bytes Tff7b72= Te6edf3dataTb4b4b4[Te6edf3offsetTb4b4b4:Tb4b4b4]
Te6edf3infoTb4b4b4[Ta5d6ff'Ta5d6ffnameTa5d6ff'Tb4b4b4] Tff7b72= Te6edf3name_bytesTff7b72.Td2a8ffdecodeTb4b4b4(Ta5d6ff'Ta5d6ffutf-8Ta5d6ff'Tb4b4b4)Tff7b72.Td2a8ffrstripTb4b4b4(Ta5d6ff'Tffea00\x00Ta5d6ff'Tb4b4b4)Tff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4)
Tff7b72return Te6edf3info
PACKETMSGSENT (0x06):
T282828
Byte 0: 0x06
Byte 1: Route Flag (0 = direct, 1 = flood)
Bytes 2-5: Tag / Expected ACK (4 bytes, little-endian)
Bytes 6-9: Suggested Timeout (32-bit little-endian, milliseconds)
PACKET_ACK (0x82):
T282828
Byte 0: 0x82
Bytes 1-6: ACK Code (6 bytes, hex)
Error Codes
T383838PACKET_ERROR (0x01) carries a single-byte error code in byte 1. Values match the T383838ERR_CODE_* constants defined in T383838examples/companion_radio/MyMesh.cpp:
ββββββββ¬βββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Code β Constant (firmware) β Description β
ββββββββΌβββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β 1 β T383838ERR_CODE_UNSUPPORTED_CMD β Unknown or unsupported command byte / sub-command β
β 2 β T383838ERR_CODE_NOT_FOUND β Target not found (channel, contact, message, etc.) β
β 3 β T383838ERR_CODE_TABLE_FULL β Internal queue or table is full β retry later β
β 4 β T383838ERR_CODE_BAD_STATE β Operation not valid in current device state (e.g. iterator alβ¦ β
β 5 β T383838ERR_CODE_FILE_IO_ERROR β Filesystem or storage I/O failure β
β 6 β T383838ERR_CODE_ILLEGAL_ARG β Invalid argument (bad length, out-of-range value, reserved fiβ¦ β
ββββββββ΄βββββββββββββββββββββββββββ΄βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Note: Error codes may vary by firmware version. Always check byte 1 of T383838PACKET_ERROR response, and treat unknown codes as generic errors.
Frame Handling
BLE implementations enqueue and deliver one protocol frame per BLE write/notification at the firmware layer.
β’ Apps should treat each characteristic write/notification as exactly one companion protocol frame
β’ Apps should still validate frame lengths before parsing
β’ Future transports or firmware revisions may differ, so avoid assuming fixed payload sizes for variable-length responses
Response Handling
1. Command-Response Pattern:
β’ Send command via RX characteristic
β’ Wait for response via TX characteristic (notification)
β’ Match response to command using sequence numbers or command type
β’ Handle timeout (typically 5 seconds)
β’ Use command queue to prevent concurrent commands
2. Asynchronous Messages:
β’ Device may send messages at any time via TX characteristic
β’ Handle T383838PACKET_MESSAGES_WAITING (0x83) by polling T383838GET_MESSAGE command
β’ Parse incoming messages and route to appropriate handlers
β’ Validate frame length before decoding
3. Response Matching:
β’ Match responses to commands by expected packet type:
β’ T383838APP_START β T383838PACKET_SELF_INFO
β’ T383838DEVICE_QUERY β T383838PACKET_DEVICE_INFO
β’ T383838GET_CHANNEL β T383838PACKET_CHANNEL_INFO
β’ T383838SET_CHANNEL β T383838PACKET_OK or T383838PACKET_ERROR
β’ T383838SEND_CHANNEL_MESSAGE β T383838PACKET_MSG_SENT
β’ T383838GET_MESSAGE β T383838PACKET_CHANNEL_MSG_RECV, T383838PACKET_CONTACT_MSG_RECV, T383838PACKET_CHANNEL_DATA_RECV, or T383838PACKET_NO_MORE_MSGS
β’ T383838SEND_CHANNEL_DATA β T383838PACKET_OK or T383838PACKET_ERROR
β’ T383838GET_BATTERY β T383838PACKET_BATTERY
4. Timeout Handling:
β’ Default timeout: 5 seconds per command
β’ On timeout: Log error, clear current command, proceed to next in queue
β’ Some commands may take longer (e.g., T383838SET_CHANNEL may need 1-2 seconds)
β’ Consider longer timeout for channel operations
5. Error Recovery:
β’ On T383838PACKET_ERROR: Log error code, clear current command
β’ On connection loss: Clear command queue, attempt reconnection
β’ On invalid response: Log warning, clear current command, proceed
Example Implementation Flow
Initialization
T282828
T8b949e# 1. Scan for MeshCore device
Te6edf3device Tff7b72= Te6edf3scan_for_deviceTb4b4b4(Ta5d6ff"Ta5d6ffMeshCoreTa5d6ff"Tb4b4b4)
T8b949e# 2. Connect to BLE GATT
Te6edf3gatt Tff7b72= Te6edf3connect_to_deviceTb4b4b4(Te6edf3deviceTb4b4b4)
T8b949e# 3. Discover services and characteristics
Te6edf3service Tff7b72= Te6edf3discover_serviceTb4b4b4(Te6edf3gattTb4b4b4, Ta5d6ff"Ta5d6ff6E400001-B5A3-F393-E0A9-E50E24DCCA9ETa5d6ff"Tb4b4b4)
Te6edf3rx_char Tff7b72= Te6edf3discover_characteristicTb4b4b4(Te6edf3serviceTb4b4b4, Ta5d6ff"Ta5d6ff6E400002-B5A3-F393-E0A9-E50E24DCCA9ETa5d6ff"Tb4b4b4)
Te6edf3tx_char Tff7b72= Te6edf3discover_characteristicTb4b4b4(Te6edf3serviceTb4b4b4, Ta5d6ff"Ta5d6ff6E400003-B5A3-F393-E0A9-E50E24DCCA9ETa5d6ff"Tb4b4b4)
T8b949e# 4. Enable notifications on TX characteristic
Te6edf3enable_notificationsTb4b4b4(Te6edf3tx_charTb4b4b4, Te6edf3on_notification_receivedTb4b4b4)
T8b949e# 5. Send AppStart command
Te6edf3send_commandTb4b4b4(Te6edf3rx_charTb4b4b4, Te6edf3build_app_startTb4b4b4(Tb4b4b4)Tb4b4b4)
Te6edf3wait_for_responseTb4b4b4(Te6edf3PACKET_SELF_INFOTb4b4b4)
Creating a Private Channel
T282828
T8b949e# 1. Generate 16-byte secret
Te6edf3secret_16_bytes Tff7b72= Te6edf3generate_secretTb4b4b4(T79c0ff16Tb4b4b4) T8b949e# Use CSPRNG
Te6edf3secret_hex Tff7b72= Te6edf3secret_16_bytesTff7b72.Td2a8ffhexTb4b4b4(Tb4b4b4)
T8b949e# 2. Build SET_CHANNEL command
Te6edf3channel_name Tff7b72= Ta5d6ff"Ta5d6ffYourChannelNameTa5d6ff"
Te6edf3channel_index Tff7b72= T79c0ff1 T8b949e# Use 1-7 for private channels
Te6edf3command Tff7b72= Te6edf3build_set_channelTb4b4b4(Te6edf3channel_indexTb4b4b4, Te6edf3channel_nameTb4b4b4, Te6edf3secret_16_bytesTb4b4b4)
T8b949e# 3. Send command
Te6edf3send_commandTb4b4b4(Te6edf3rx_charTb4b4b4, Te6edf3commandTb4b4b4)
Te6edf3response Tff7b72= Te6edf3wait_for_responseTb4b4b4(Te6edf3PACKET_OKTb4b4b4)
T8b949e# 4. Store secret locally
Te6edf3store_channel_secretTb4b4b4(Te6edf3channel_indexTb4b4b4, Te6edf3secret_hexTb4b4b4)
Sending a Message
T282828
T8b949e# 1. Build channel message command
Te6edf3channel_index Tff7b72= T79c0ff1
Te6edf3message Tff7b72= Ta5d6ff"Ta5d6ffHello, MeshCore!Ta5d6ff"
Te6edf3timestamp Tff7b72= Tffa657intTb4b4b4(Te6edf3timeTff7b72.Td2a8fftimeTb4b4b4(Tb4b4b4)Tb4b4b4)
Te6edf3command Tff7b72= Te6edf3build_channel_messageTb4b4b4(Te6edf3channel_indexTb4b4b4, Te6edf3messageTb4b4b4, Te6edf3timestampTb4b4b4)
T8b949e# 2. Send command
Te6edf3send_commandTb4b4b4(Te6edf3rx_charTb4b4b4, Te6edf3commandTb4b4b4)
Te6edf3response Tff7b72= Te6edf3wait_for_responseTb4b4b4(Te6edf3PACKET_MSG_SENTTb4b4b4)
Receiving Messages
T282828
Tff7b72def Td2a8ffon_notification_receivedTb4b4b4(Te6edf3dataTb4b4b4)Tb4b4b4:
Te6edf3packet_type Tff7b72= Te6edf3dataTb4b4b4[T79c0ff0Tb4b4b4]
Tff7b72if Te6edf3packet_type Tff7b72== Te6edf3PACKET_CHANNEL_MSG_RECV Tff7b72or Te6edf3packet_type Tff7b72== Te6edf3PACKET_CHANNEL_MSG_RECV_V3Tb4b4b4:
Te6edf3message Tff7b72= Te6edf3parse_channel_messageTb4b4b4(Te6edf3dataTb4b4b4)
Te6edf3handle_channel_messageTb4b4b4(Te6edf3messageTb4b4b4)
Tff7b72elif Te6edf3packet_type Tff7b72== Te6edf3PACKET_MESSAGES_WAITINGTb4b4b4:
T8b949e# Poll for messages
Te6edf3send_commandTb4b4b4(Te6edf3rx_charTb4b4b4, Te6edf3build_get_messageTb4b4b4(Tb4b4b4)Tb4b4b4)
Best Practices
1. Connection Management:
β’ Implement auto-reconnect with exponential backoff
β’ Handle disconnections gracefully
β’ Store last connected device address for quick reconnection
2. Secret Management:
β’ Always use cryptographically secure random number generators
β’ Store secrets securely (encrypted storage)
β’ Never log or transmit secrets in plain text
3. Message Handling:
β’ Send T383838CMD_SYNC_NEXT_MESSAGE when T383838PUSH_CODE_MSG_WAITING is received
β’ Implement message deduplication to avoid displaying the same message twice
4. Channel Management:
β’ Fetch all channel slots even if you encounter an empty slot
β’ Ideally save new channels into the first empty slot
5. Error Handling:
β’ Implement timeouts for all commands (typically 5 seconds)
β’ Handle T383838RESP_CODE_ERR responses appropriately
Troubleshooting
Connection Issues
β’ Device not found: Ensure device is powered on and advertising
β’ Connection timeout: Check Bluetooth permissions and device proximity
β’ GATT errors: Ensure proper service/characteristic discovery
Command Issues
β’ No response: Verify notifications are enabled, check connection state
β’ Error responses: Verify command format and check error code
β’ Timeout: Increase timeout value or try again
Message Issues
β’ Messages not received: Poll T383838GET_MESSAGE command periodically
β’ Duplicate messages: Implement message deduplication using timestamp/content as a unique id
β’ Message truncation: Send long messages as separate shorter messages
Served by rngit 1.5.4 - Generated in 0.09s